/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package edu.ncsu.csc573.project1.peer;

import edu.ncsu.csc573.project1.util.Constants;
import edu.ncsu.csc573.project1.util.Constants.PPMethod;
import edu.ncsu.csc573.project1.util.Constants.Status;
import edu.ncsu.csc573.project1.util.HeaderField;
import edu.ncsu.csc573.project1.util.PPRequest;
import edu.ncsu.csc573.project1.util.PPResponse;
import edu.ncsu.csc573.project1.util.Rfc;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.DateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.swing.JEditorPane;

/**
 *
 * @author svpendse1
 */
public class UploadServer implements Runnable {
    ServerSocket serverSocket;      //The server socket
    Socket clientSocket;            //The client socket connecting to this server
    String osName;
    ConcurrentHashMap<Integer, Rfc> rfcMap;   //The RFC map (from the peer) (thread-safe)
    int portNumber;                 //The server port number
    JEditorPane uploadEditorPane;
    public UploadServer(ConcurrentHashMap<Integer, Rfc> rfcMap, int portNumber,
                        JEditorPane uploadEditorPane) {
        this.uploadEditorPane = uploadEditorPane;
        this.rfcMap = rfcMap;
        this.portNumber = portNumber;
        this.osName = System.getProperty("os.name");
        try {
            serverSocket = new ServerSocket(portNumber);
        } catch (IOException e) {
            System.err.println("Could not listen on port : " + portNumber);
        }
        String update = "Upload server ready on port " + portNumber + "\n";
        uploadEditorPane.setText(uploadEditorPane.getText() + update);

    }

    public void run() {
        while (true) {
            try {
                clientSocket = serverSocket.accept();
                new Thread(new UploadHandler(clientSocket)).start();
            } catch (IOException e) {
                System.err.println("Accept failed");
            }
            
        }
    }

    class UploadHandler implements Runnable {
        Socket clientSocket;
        DataOutputStream out;
        DataInputStream in;
        boolean active;
        
        UploadHandler(Socket clientSocket) {
            this.active = true;
            this.clientSocket = clientSocket;
            try {
                out = new DataOutputStream(this.clientSocket.getOutputStream());
                in = new DataInputStream(this.clientSocket.getInputStream());
            } catch (IOException e) {
                System.err.println("Failed to create reader/writer objects for the client");
            }
            Iterator it = rfcMap.entrySet().iterator();

            while (it.hasNext()) {
                @SuppressWarnings("unchecked")
                Map.Entry<Integer, Rfc> pairs = (Map.Entry<Integer, Rfc>) it.next();
                //System.out.println(pairs.getKey().intValue());
            }
            String update = "A new connection at the Upload Server from " + clientSocket.getInetAddress().getHostAddress() + "\n";
            uploadEditorPane.setText(uploadEditorPane.getText() + update);
        }
        
        public void run() {
            while (active) {
                try {
                    respondToRequest();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } 
        }

        public void respondToRequest() throws Exception {
            while (in.available() == 0) {
            }
            byte[] input = new byte[in.readInt()];
            in.readFully(input);
            String message = new String(input);

            PPRequest request = PPRequest.parseFromString(message);

            String currentDate = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG).format(new Date());
            if (request == null) {
                //Bad request
                PPResponse response = new PPResponse.PPResponseBuilder().version(Constants.kVersion).status(Status.BAD_REQUEST).dateField(new HeaderField("Date", currentDate)).osField(new HeaderField("OS", osName)).lastModifiedField(new HeaderField("Last-Modified", DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG).format(currentDate))).contentLengthField(new HeaderField("Content-Length", "0")).contentTypeField(new HeaderField("Content-Type", Constants.kContentType)).data("").build();
                out.writeInt(response.getResponseMessage().length());
                out.writeBytes(response.getResponseMessage());
                String update = "Bad Request";
                uploadEditorPane.setText(uploadEditorPane.getText() + update);
                return;
            } else if (! request.getVersion().equals("P2P-CI/1.0")) {
                //Unsupported version  
                PPResponse response = new PPResponse.PPResponseBuilder().version(Constants.kVersion).status(Status.P2P_CI_VERSION_NOT_SUPPORTED).dateField(new HeaderField("Date", currentDate)).osField(new HeaderField("OS", osName)).lastModifiedField(new HeaderField("Last-Modified", DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG).format(currentDate))).contentLengthField(new HeaderField("Content-Length", "0")).contentTypeField(new HeaderField("Content-Type", Constants.kContentType)).data("").build();
                out.writeInt(response.getResponseMessage().length());
                out.writeBytes(response.getResponseMessage());
                String update = "Unsupported Version";
                uploadEditorPane.setText(uploadEditorPane.getText() + update);
                return;
            }
            
            
            if (request.getExchangeMethod() == PPMethod.GET) {
                int rfcNumber = request.getRfcNumber();
                String update = "Request for RFC " + rfcNumber + " from " + clientSocket.getInetAddress().getHostAddress() + "\n";
                uploadEditorPane.setText(uploadEditorPane.getText() + update);
                if (rfcMap.containsKey(new Integer(rfcNumber))) {
                    //RFC Found at Peer
                    Rfc rfc = rfcMap.get(new Integer(rfcNumber));
                    String lastModifiedDate = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG).format(rfc.getLastModified());
                    
                    int contentLength = rfc.getContent().length();
                     
                    PPResponse response = new PPResponse.PPResponseBuilder().version(Constants.kVersion).status(Status.OK).dateField(new HeaderField("Date", currentDate)).osField(new HeaderField("OS", osName)).lastModifiedField(new HeaderField("Last-Modified", lastModifiedDate)).contentLengthField(new HeaderField("Content-Length", "" + contentLength)).contentTypeField(new HeaderField("Content-Type", Constants.kContentType)).data(rfc.getContent()).build();

                    out.writeInt(response.getResponseMessage().length());
                    out.writeBytes(response.getResponseMessage());
        
                    update = "Sent RFC " + rfcNumber + " to " + clientSocket.getInetAddress().getHostAddress() + "\n";
                    uploadEditorPane.setText(uploadEditorPane.getText() + update);
                } else {
                    //RFC Not Found at Peer
                     update = "RFC " + rfcNumber + " requested by " + clientSocket.getInetAddress().getHostAddress() + " not found.\n";
                     uploadEditorPane.setText(uploadEditorPane.getText() + update);

                    PPResponse response = new PPResponse.PPResponseBuilder().version(Constants.kVersion).status(Status.NOT_FOUND).dateField(new HeaderField("Date", currentDate)).osField(new HeaderField("OS", osName)).build();
                    out.writeInt(response.getResponseMessage().length());
                    out.writeBytes(response.getResponseMessage());
                }
                
            } else if (request.getExchangeMethod() == PPMethod.CLOSE) {
                this.active = false;
                try {
                    in.close();
                    out.close();
                    clientSocket.close();
                    String update = "Closed connection to " + clientSocket.getInetAddress().getHostAddress() + "\n";
                    uploadEditorPane.setText(uploadEditorPane.getText() + update);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
                
        }
    } 
}
